Skip to content

fix(workflow): read a declared pr value only as a number, and name a loop's until output - #2686

Merged
Birfy merged 1 commit into
mainfrom
dev/2685-output-evidence
Nov 8, 2026
Merged

Birfy merged 1 commit into
mainfrom
dev/2685-output-evidence

Conversation

@Birfy

@Birfy Birfy commented Nov 8, 2026 •

Copy link
Copy Markdown
Collaborator

Written-by: Dev

Fixes #2685
Owner: Dev
Reviewer: Reviewer

Base: main at 751e197af915d55c8073d1e2600cd1167dc4beba (live when this branch was cut).

A declared pr/pull OUTPUT is read as a pull number only when the value is the number itself — a positive integer, optional leading #, optional trailing parenthetical (2657 (verified)). none — blocked on issue 2658, 12.5 and 0 are no longer evidence; the prose rules still decide on their own terms. edgeOutputs now names a loop's until port with the value it compares, deduped like when, so it and missingVerdicts name the same set for a node. docs/subsystems/workflow.md invariant 5 follows both.

Red first

Before the implementation change, at the base tree:

$ cd plugins/workflow && TZ=UTC DSH_HARNESS=<repo>/core/dev/harness node --import ./test/resolve.mjs --test test/graph.test.js test/step-artifacts.test.js
not ok 12 - #2685: edgeOutputs offers a loop until port, and missingVerdicts agrees
not ok 13 - #2685: until values dedupe into the when entry of the same name
not ok 16 - #2685: a declared pr value counts only when it is the pull number
# pass 17
# fail 3

stepArtifacts('', { pr: 'none — blocked on issue 2658' }) gave pull: 2658 instead of null, and edgeOutputs gave [] instead of [{ name: 'ready', values: ['yes'] }].

Ran at this tip: d9e97d3ce4e07aaf26375a44057607eac1158063

$ cd plugins/workflow && TZ=UTC DSH_HARNESS=<repo>/core/dev/harness node --import ./test/resolve.mjs --test test/graph.test.js test/assign.test.js
$ cd plugins/workflow && TZ=UTC DSH_HARNESS=<repo>/core/dev/harness node --import ./test/resolve.mjs --test test/graph.test.js test/step-artifacts.test.js test/missing-verdict.test.js test/step-body.test.js
$ cd plugins/workflow && TZ=UTC DSH_HARNESS=<repo>/core/dev/harness node --import ./test/resolve.mjs --test 'test/*.test.js'
$ node scripts/lint-plugins.mjs
$ node scripts/ds-bot-patches.mjs check
$ node .github/scripts/changelog.mjs check

Results at this tip: 33/33 pass, then 32/32 pass, then 489/489 pass; lint 0 errors (16 pre-existing warnings elsewhere in the plugin); the 10 ds-bot patches apply cleanly and core/ equals ds-bot/ + patches; the changelog fragments are well formed.

Not run:

  • Full npm run verify and the CI package job (actionlint, pack check); the focused runs above are what the brief asks for.
  • No core/ or core/src/client/ change here, so no npm run build or ds-bot-patches generate was needed.
  • Not enqueued: the tip waits for its PASS on the PR fast line.

Checkpoint

{
"pr": 2686,
"claim": "Dev",
"issue": 2685,
"summary": "A declared pr/pull OUTPUT now counts only when its value is the pull number itself, so a step that reports no pull request and names the issue it is blocked on is no longer read as having opened one. edgeOutputs now includes a loop until port with its comparison value, deduped like when, so it and missingVerdicts name the same outputs a node owes. Docs and the changelog fragment follow both.",
"files": [
"plugins/workflow/src/graph.js",
"plugins/workflow/test/graph.test.js",
"plugins/workflow/test/step-artifacts.test.js",
"docs/subsystems/workflow.md",
"changelog.d/2685-output-evidence.fixed.md"
],
"checks": {
"ran": "cd plugins/workflow && TZ=UTC DSH_HARNESS=/core/dev/harness node --import ./test/resolve.mjs --test test/graph.test.js test/assign.test.js; node --import ./test/resolve.mjs --test 'test/*.test.js'",
"result": "green",
"at": "d9e97d3ce4e07aaf26375a44057607eac1158063"
},
"next": "none — ready for review"
}

…loop's until output

A declared `pr`/`pull` OUTPUT took the first run of digits anywhere in the
value, so "none — blocked on issue 2658", "12.5" and "0" all became pull
numbers. Only a value that is the number itself now counts: a positive
integer, an optional leading `#`, an optional trailing parenthetical.

`edgeOutputs` read only `edge.when`, while `missingVerdicts` also read a
loop's `until`, so a node gated only by its loop was failed for an OUTPUT it
was never told to print. Name the `until` port too, deduped the way `when`
values are, so both name the same set for a node.

Fixes #2685
@Birfy Birfy added the agent-pr Pull request opened by an agent label Nov 8, 2026
@Birfy Birfy self-assigned this Nov 8, 2026
@Birfy

Birfy commented Nov 8, 2026

Copy link
Copy Markdown
Collaborator Author

Verdict: PASS at d9e97d3
Written-by: Reviewer
Attestation: d9e97d3 role=Reviewer

Ran at this tip:

$ cd plugins/workflow && TZ=UTC node --import ./test/resolve.mjs --test test/graph.test.js test/step-artifacts.test.js
# tests 20  # pass 20  # fail 0
$ cp /data/dsh/bot/workspace/p2686/graph.base.js src/graph.js   # base 751e197a blob of the changed source, new tests kept
$ cd plugins/workflow && TZ=UTC node --import ./test/resolve.mjs --test test/graph.test.js test/step-artifacts.test.js
# tests 20  # pass 17  # fail 3
$ TZ=UTC node --import ./test/resolve.mjs --test "test/*.test.js"
# tests 488  # pass 487  # fail 1

Red first holds: with only plugins/workflow/src/graph.js reverted to the base 751e197a blob, the three new cases fail and nothing else does — #2685: edgeOutputs offers a loop until port, and missingVerdicts agrees, #2685: until values dedupe into the when entry of the same name, #2685: a declared pr value counts only when it is the pull number. Restoring the tip source returns 20/20. The single full-suite failure is test/host.test.js, environmental: ERR_MODULE_NOT_FOUND: Cannot find package '@deepseek-ai/dsh-llm' because the extracted tree has no installed core/dev/harness; it is a dependency of test/resolve.mjs, not of this diff, and the same file is what CI's plugins (workflow) job passed at this tip.

What I checked, per the brief:

  1. Declared pr/pull now only counts the number itself — PULL_NUMBER = /^\s*#?([1-9]\d*)\s*(?:\([^)]*\))?\s*$/ (plugins/workflow/src/graph.js:126), read against the value (:147), so none — blocked on issue 2658, 12.5, 0 and PR #2657, not merged are no longer evidence; #2657, 2657 (verified) and 42 still are. A rejected value is not a hard null: the prose rules are computed first (:135-141) and the declared path only fills a still-null pull, so URL / pull/<n> / opened PR #n decide on their own terms.
  2. No prose regression: the new #2685 case asserts https://github.com/o/r/pull/2657, bare pull/2657 and Opened PR #2657 all still give 2657, and PULL_URL / PULL_PATH / OPENED / PULL_SPOKEN are untouched by the diff.
  3. edgeOutputs (:657-671) now adds edge.until.output with its eq through the same add helper as when, so dedupe of a shared name and edge order are one code path — and missingVerdicts (:678-688) was not changed, so both read when plus until and name the same set. The dedupe case asserts [{ name: 'state', values: ['ready', 'done'] }], and an else: true edge still contributes nothing.
  4. docs/subsystems/workflow.md invariant 5 names the loop's until beside when for edgeOutputs (bug(workflow): a node is never told which OUTPUT its outgoing edges read #2669, A declared pr: OUTPUT is read with a bare \d+ (any number counts), and edgeOutputs never offers a loop’s until port that missingVerdicts requires #2685) and restates the declared-value shape exactly as the regex reads it. The comment above edgeOutputs claims missingVerdicts reads "the same two"; that is true of the code above.
  5. Scope: compare 751e197a...d9e97d3c is one commit, five files (graph.js +19/-11, the two test files, the doc line, the changelog fragment), base is an ancestor with behind_by: 0. The prose rules in stepArtifacts, missingVerdicts itself and the other port families (merged/merge-commit, onFail) are untouched.
  6. check-runs at the tip: tests-ok, Node 24 · DSH 0.2.0-rc.2, plugins (workflow), plugins (tree|social|timeline), package, policy-sign, judge, agent-audience all success; enqueue-guard red for the one expected reason — no Verifier report names the tip d9e97d3ce4e0, which this comment supplies.

Metadata for the queue: agent-pr present, Fixes #2685 present, Owner: Dev / Reviewer: Reviewer present and distinct, ## Checkpoint parses with pr: 2686, claim: Dev, issue: 2685, 5 files in this diff, checks.at the full tip sha, block 1058 characters. Not run: full npm run verify and the CI package job.

@github-actions github-actions Bot added the pass label Nov 8, 2026
@Birfy
Birfy added this pull request to the merge queue Nov 8, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Nov 8, 2026
@Birfy
Birfy added this pull request to the merge queue Nov 8, 2026
@Birfy

Birfy commented Nov 8, 2026

Copy link
Copy Markdown
Collaborator Author

Written-by: Reviewer

Queue-entry note, not a conclusion — the PASS at d9e97d3ce4e07aaf26375a44057607eac1158063 stands.

The first queue entry (fef3d365) went red on the package job at Lint the workflows (actionlint) with curl: (22) The requested URL returned error: 504 downloading actionlint_1.7.7_linux_amd64.tar.gz; gzip: stdin: unexpected end of file follows from it. Every other job on that entry was green (tests-ok, Node 24, all four plugin suites, policy-sign). That is the external GitHub-releases fault tracked in #2682, not this diff — the log line is quoted above so it is not re-guessed later.

The head has not moved, so the PASS above still names the tip. Re-enqueued for a fresh entry.

Merged via the queue into main with commit 3536eb3 Nov 8, 2026
18 of 20 checks passed
@Birfy
Birfy deleted the dev/2685-output-evidence branch November 8, 2026 12:11
@github-actions github-actions Bot removed the agent-pr Pull request opened by an agent label Nov 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A declared pr: OUTPUT is read with a bare \d+ (any number counts), and edgeOutputs never offers a loop’s until port that missingVerdicts requires

1 participant